#include "Messenger.h"
#include "Timer.h"
#include "util/Shared_ptr.h"



void Boon::Messenger::OnInitialize()
{
	m_UsedEventQueue = m_UsedEventQueueSN = 0;
}

void Boon::Messenger::OnDestroy()
{

}

bool Boon::Messenger::Register( const EventType& id, const EventListener& listener )
{
	// get all registered listeners to the given event-id
	ListListeners& l = m_Listeners[id];
	auto iter = l.begin();
	auto end = l.end();

	// check if listener has already been registered to this event
	for( ; iter != end; ++iter )
	{
		if( (*iter) == listener )
			return false;
	}
	l.push_back( listener );
	return true;
}

bool Boon::Messenger::Remove( const EventType& id, const EventListener& listener )
{
	auto resultList = m_Listeners.find( id );
	// given event is registered in listener map?!
	if( resultList != m_Listeners.end() )
	{
		// get all registered listeners to the given event-id
		ListListeners& l = resultList->second;
		auto iter = l.begin();
		auto end = l.end();
		for( ; iter != end; ++iter )
		{
			// remove listener and return success
			if( (*iter) == listener )
			{
				l.erase( iter );
				return true;
			}
		}
	}
	return false;
}

bool Boon::Messenger::Bang( const IMessengerEventStrong& theEvent )
{
	auto result = m_Listeners.find( theEvent->GetEventID() );
	// check if event has already been registered
	if( result != m_Listeners.end() )
	{
		auto iter = result->second.begin();
		auto end = result->second.end();

		// call the delegates (listeners) and pass event data
		while( iter != end )
		{
			(*iter)( theEvent );
			++iter;
		}
		return true;
	}
	return false;
}

bool Boon::Messenger::BangQueued( const IMessengerEventStrong& theEvent, MessageQueueType type )
{
	return BangTimed( theEvent, .0f, type );
}

bool Boon::Messenger::BangTimed( const IMessengerEventStrong& theEvent, const float delay, MessageQueueType type )
{
	auto result = m_Listeners.find( theEvent->GetEventID() );
	uint_32 qIndex = GetQueueIndex( type );
	if( result != m_Listeners.end() )
	{
		// enqueue event with .0f time delay
		m_EventQueue[qIndex].push_back(
			std::make_pair<float, IMessengerEventStrong>( delay, theEvent ) );
		return true;
	}
	return false;
}

bool Boon::Messenger::AbortEvent( const EventType& id, const bool allOfType, MessageQueueType type )
{
	auto result = m_Listeners.find( id );
	bool returnVal = false;
	uint_32 qIndex = GetQueueIndex( type );

	// registered listeners to event id?!
	if( result != m_Listeners.end() )
	{
		ListQueuedEvents& l = m_EventQueue[qIndex];
		auto iter = l.begin();
		auto end = l.end();

		// check all events registered in queue
		while( iter != end )
		{
			// advance iteration and save current for deletion
			auto cur = iter;
			++iter;

			// remove on matching event id
			if( cur->second->GetEventID() == id )
			{
				l.erase( cur );
				returnVal = true;
				if( !allOfType ) break;
			}
		}
	}
	return returnVal;
}


Boon::uint_32 Boon::Messenger::GetQueueIndex( Boon::MessageQueueType type )
{
	uint_32 usedQueue = m_UsedEventQueue;
	if( type == QUEUETYPE_SCENENODE ) 
		usedQueue = m_UsedEventQueueSN;
	return ( usedQueue + (uint_32)type ); //offset for queue type
}

void Boon::Messenger::ProcessQueuedEvents( unsigned int maxMil )
{
	static uint_32 lastTime = Timer::TimeStamp();
	ProcessEvents( maxMil, m_UsedEventQueue, lastTime, QUEUETYPE_LOGIC );
}

void Boon::Messenger::ProcessQueuedEventsSceneNode( unsigned int maxMil )
{
	static uint_32 lastTime = Timer::TimeStamp();
	ProcessEvents( maxMil, m_UsedEventQueueSN, lastTime, QUEUETYPE_SCENENODE );
}

void Boon::Messenger::ProcessEvents( uint_32 maxMil, unsigned char& usedEventQueue, uint_32& lastTime, MessageQueueType type )
{
	// calculate dt (time since last call of update() )
	uint_32 now = Timer::TimeStamp();
	float dt = float( ( lastTime - now ) * .001f );
	lastTime = now;

	// get queue of past update iteration
	// prevent infinity loop of events 
	ListQueuedEvents& l = m_EventQueue[GetQueueIndex( type )]; 
	// switch between index 0 / 1 (changing m_UsedEventQueue or m_UsedEventQeueuSN depending on type)
	usedEventQueue = ( usedEventQueue + 1 ) % 2;
	uint_32 qIndexNextIteration = GetQueueIndex( type );
	m_EventQueue[qIndexNextIteration].clear(); // clear queue that will be used in next update iteration

	auto iter = l.begin();
	auto end = l.end();

	while( iter != end )
	{
		// advance iteration and save current for deletion
		auto cur = iter;
		++iter;

		cur->first -= dt;
		// Bang and remove if time exceeded
		if( cur->first <= .0f )
		{
			Bang( cur->second );
			l.erase( cur );
		}
		// Break routine if exceeded maximum processing time
		if( Timer::TimeStamp() - now >= maxMil )
			break;
	}
	if( !l.empty() ) // remaining events?! hand over to current set queue
		m_EventQueue[qIndexNextIteration].splice( m_EventQueue[qIndexNextIteration].begin(), l );
}
